﻿package 
{
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.net.FileReference;
	import flash.text.TextField;
	import flash.ui.Keyboard;
	import no.doomsday.aronning.utilities.parameters.RangedParameter;
	import no.doomsday.aronning.utilities.parameters.Parameter;
	import no.doomsday.aronning.engines.verlets.constraints.PointConstraint;
	import no.doomsday.aronning.engines.verlets.constraints.StickConstraint;
	import no.doomsday.aronning.engines.verlets.geometry.Cloth;
	import no.doomsday.aronning.engines.verlets.geometry.Hull;
	import no.doomsday.aronning.engines.verlets.utils.CollissionTests;
	import no.doomsday.aronning.engines.verlets.Verlet;
	import no.doomsday.aronning.engines.verlets.VerletSystem;
	
	
	public class Main extends Sprite
	{			
		private var system:VerletSystem
		private var mouseConstraint:PointConstraint;
		private var simulating:Boolean = false;
		private var constraintStiffness:Parameter;
		private var clothStiffnessParam:RangedParameter;
		private var tempMouseConstraint:PointConstraint;
		private var hull:Hull;
		private var renderHull:Boolean = false;
		private var updating:Boolean = true;
		private var makingStick:Boolean = false;
		private var startVerlet:Verlet;
		private var tempVerlet:Verlet;
		private var tempStick:StickConstraint;
		private var frictionParam:RangedParameter;
		private var mouseStiffnessParam:RangedParameter = new RangedParameter(0, 1, 1);
		public function Main():void
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		
		private function init(e:Event = null):void 
		{		
			
			stage.frameRate = 60;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			
			system = new VerletSystem();
			system.selfClean = true;
			
			
			clothStiffnessParam = new RangedParameter(0, 1, 0.7, "Cloth stiffness");
			frictionParam = new RangedParameter(0, 1, 0.2, "Bounds friction");
			
			system.addForce(0, 0); //gravity
			system.addRectangleConstraint(new Rectangle(0, 0, stage.stageWidth, stage.stageHeight), true, frictionParam ); //no leaving the stage
			
			var cloth:Cloth = system.createCloth(new Rectangle(0, 0, 300, 300), clothStiffnessParam,10,10,true);
			
			system.addPointConstraint(20, 20, cloth.corners[0]);
			system.addPointConstraint(380, 20, cloth.corners[1]);
			system.addPointConstraint(380, 380, cloth.corners[2]);
			system.addPointConstraint(20, 380, cloth.corners[3]);
			
			hull = new Hull(cloth.points);
						
			removeEventListener(Event.ADDED_TO_STAGE, init);
			stage.addEventListener(Event.ENTER_FRAME, update);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, dragPoint);
			stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
			stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
		}
		
		private function onMouseWheel(e:MouseEvent):void 
		{
			mouseStiffnessParam.value += e.delta / 100;
			var exp:Number = Math.log(1-mouseStiffnessParam.value);
			clothStiffnessParam.value = -exp/7;
			trace(clothStiffnessParam.value);
		}
		
		private function onKeyDown(e:KeyboardEvent):void 
		{
			if (e.charCode == String("h").charCodeAt()) renderHull = !renderHull;
			if (e.keyCode == Keyboard.SPACE) updating = !updating;
			if (e.charCode == String("v").charCodeAt()) system.addPoint(mouseX, mouseY);
			if (e.charCode == String("r").charCodeAt()) softReset();
			if (e.charCode == String("s").charCodeAt()) beginNewStick();
		}
		
		private function onKeyUp(e:KeyboardEvent):void 
		{
			if (e.charCode == String("s").charCodeAt()) finishStick();
		}
		
		private function beginNewStick():void
		{
			if (!makingStick) {
				stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
				startVerlet = system.getClosestVerletToPoint(mouseX, mouseY);
				tempVerlet = new Verlet(mouseX, mouseY);
				tempStick = system.addStickConstraint(startVerlet, tempVerlet, new Parameter(0), true, 0, true);
				makingStick = true;
				stage.addEventListener(MouseEvent.MOUSE_MOVE, onMakeStickMouseMove);
				
			}
		}
		
		private function onMakeStickMouseMove(e:MouseEvent):void 
		{
			try{
				var nearestVerlet:Verlet = system.getClosestVerletToPoint(mouseX, mouseY);
				if (nearestVerlet == startVerlet) return;
				tempStick.b = nearestVerlet;
			}catch (e:Error) {
				
			}
		}
		
		private function finishStick():void
		{
			if (tempStick.b == tempVerlet||tempStick.b == tempStick.a) {
				system.removeConstraint(tempStick);
			}else {
				
				tempStick.setRestlengthFromTargets();
				tempStick.stiffness = clothStiffnessParam;
			}
			makingStick = false;
			stage.removeEventListener(KeyboardEvent.KEY_UP, onKeyUp);
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMakeStickMouseMove);
		}
		
		
		private function softReset():void
		{
			system.reset();
			system.addForce(0, 60); //gravity
			system.addRectangleConstraint(new Rectangle(0, 0, stage.stageWidth, stage.stageHeight), true, frictionParam ); //no leaving the stage
		}
		
		private function dragPoint(e:MouseEvent):void 
		{
			try{
				var verlet:Verlet = system.getClosestVerletToPoint(stage.mouseX, stage.mouseY);
				if(!e.ctrlKey){
					tempMouseConstraint = system.addPointConstraint(mouseX, mouseY, verlet);
					stage.addEventListener(MouseEvent.MOUSE_MOVE, updateMouseConstraint);
					stage.addEventListener(MouseEvent.MOUSE_UP, releaseMouse);
				}else {
					system.removePoint(verlet);
				}
			}catch (e:Error) {
				
			}
		}
		
		private function releaseMouse(e:MouseEvent):void 
		{
			if(!e.shiftKey){
				system.removeConstraint(tempMouseConstraint);
			}else {
				tempMouseConstraint = system.addPointConstraint(0, 0);
			}
			
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, updateMouseConstraint);
		}
		
		private function updateMouseConstraint(e:MouseEvent):void 
		{
			tempMouseConstraint.x = stage.mouseX;
			tempMouseConstraint.y = stage.mouseY;
		}
		
		private function killPoint(e:MouseEvent):void 
		{
			try{
				var verlet:Verlet = system.getClosestVerletToPoint(stage.mouseX, stage.mouseY);
				system.removePoint(verlet);
			}catch (e:Error) {
				
			}
		}
		
		private function update(e:Event):void {
						
			if(updating){
				var delta:Number = 3 / stage.frameRate; //linear timestep
				system.update(delta,10);
			}
			graphics.clear();
			system.render(graphics);
			if (renderHull) hull.render(graphics);
		}
	}
}